#include "ViewChange.hpp"

using namespace BFT::order::messages;

ViewChange::ViewChange(uint32_t _view, uint32_t _ccpseq, uint32_t _scpseq, uint32_t _pseq, uint32_t _ppseq,
  		       boost::shared_ptr<BFT::order::statemanagement::CheckPointState> _committedCP,
		       boost::shared_ptr<BFT::order::statemanagement::CheckPointState> _stableCP,
		       std::vector<boost::shared_ptr<BFT::messages::CertificateEntry> > _entries,
		       std::vector<boost::shared_ptr<BFT::messages::HistoryDigest> >_hist,
		       uint32_t _sendingReplica)
	: BFT::messages::MacArrayMessage::MacArrayMessage ( VIEW_CHANGE, 
							    computeSize(_committedCP, _stableCP, _entries),
							    _sendingReplica,
							    BFT::Parameters::getOrderCount() )
{
	viewNo = _view;
	cpSeqNo = _ccpseq;
	stableCPSeqNo = _scpseq;
	pSeqNo = _pseq;
	ppSeqNo = _ppseq;
	committedCP = _committedCP;
	stableCP = _stableCP;
	batch = _entries;
	histories = _hist;
	
	int bytes_size;
	boost::shared_ptr<char> bytes = getBytes(bytes_size);
	int offset = getOffset();
	
	//write down the view number
	*(uint32_t*)(bytes.get() + offset) = htonl(viewNo);
	offset += VIEW_NUMBER_SIZE; 	
	
	//write down the seqno of the committed CP
	*(uint32_t*)(bytes.get() + offset) = htonl(cpSeqNo);
	offset += SEQUENCE_NUMBER_SIZE; 	

	//write down the seqno of the stable CP
	*(uint32_t*)(bytes.get() + offset) = htonl(stableCPSeqNo);
	offset += SEQUENCE_NUMBER_SIZE; 	

	//write down the seqno of the last prepared
	*(uint32_t*)(bytes.get() + offset) = htonl(pSeqNo);
	offset += SEQUENCE_NUMBER_SIZE; 	

	//write down the seqno of the hast preprepared
	*(uint32_t*)(bytes.get() + offset) = htonl(ppSeqNo);
	offset += SEQUENCE_NUMBER_SIZE; 	

	int temp_bytes_size;
	boost::shared_ptr<char> temp_bytes = committedCP.get()->getBytes(temp_bytes_size);
	
	//write down the size of the committed checkpoint
	*(uint32_t*)(bytes.get() + offset) = htonl(temp_bytes_size);
	offset += CHECKPOINTSTATE_SIZE_SIZE; 
	
	//write down the committed checkpoint
	memcpy( (void*)(bytes.get()+offset), (temp_bytes.get()), temp_bytes_size );
	offset += temp_bytes_size;
	
	temp_bytes = stableCP.get()->getBytes(temp_bytes_size);
	
	//write down the size of the stable checkpoint
	*(uint32_t*)(bytes.get() + offset) = htonl(temp_bytes_size);
	offset += CHECKPOINTSTATE_SIZE_SIZE; 
	
	//write down the stable checkpoint
	memcpy( (void*)(bytes.get()+offset), (temp_bytes.get()), temp_bytes_size );
	offset += temp_bytes_size;
	
	//write down the number of certificate entries in the batch
	*(uint16_t*)(bytes.get() + offset) = htons((uint16_t)batch.size());
	offset += ENTRY_COUNT_SIZE; 	
	
//	fprintf(stderr,"ViewChange::ViewChange says: before starting to loop over %d batches and histories\n", (int)batch.size());
	
	//loop over the batches and the histories vectors
	for(unsigned int i = 0 ; i<batch.size() ; i++ )
	{
//		fprintf(stderr, "iteration %d\n",i);
		temp_bytes = batch.at(i).get()->getBytes(temp_bytes_size);
		//write down the size of the certififcate entry
//		fprintf(stderr, "certificate entry size %d\n",temp_bytes_size);
		*(uint32_t*)(bytes.get() + offset) = htonl(temp_bytes_size);
		offset += CERTIFICATE_ENTRY_SIZE_SIZE;

//		fprintf(stderr, "certificate\n");	
		//write down the certificate entry 	
		memcpy( (void*)(bytes.get()+offset) , (const void*)(temp_bytes.get()) , temp_bytes_size );
		offset += temp_bytes_size;
		
//		fprintf(stderr, "history\n");		
		temp_bytes = histories.at(i).get()->getBytes();
		
		//write down the related history digest
		memcpy( (void*)(bytes.get()+offset) , (const void*)(temp_bytes.get()) , DIGEST_SIZE );
		offset += DIGEST_SIZE;		
	}
//	fprintf(stderr,"ViewChange::ViewChange says: right after having looped over %d batches and histories\n", (int)batch.size());
	
}

ViewChange::ViewChange(boost::shared_ptr<char> _bytes, int _bytes_size)
	: BFT::messages::MacArrayMessage(_bytes, _bytes_size)
{
	
	if( getTag() != VIEW_CHANGE )
	{
		fprintf(stderr, "unable to build a ViewChange ffom bytes, different tag.\n");
		exit(153);	
	}	
	
	int offset = getOffset();
	
	//read the view number
	viewNo = ntohl( *(uint32_t*)(_bytes.get()+offset) );
	offset += VIEW_NUMBER_SIZE;

	//read the committed CP seqno
	cpSeqNo = ntohl( *(uint32_t*)(_bytes.get()+offset) );
	offset += SEQUENCE_NUMBER_SIZE;

	//read the stable CP seqno
	stableCPSeqNo = ntohl( *(uint32_t*)(_bytes.get()+offset) );
	offset += SEQUENCE_NUMBER_SIZE;

	//read the last prepared seqno
	pSeqNo = ntohl( *(uint32_t*)(_bytes.get()+offset) );
	offset += SEQUENCE_NUMBER_SIZE;	

	//read the last preprepared seqno
	ppSeqNo = ntohl( *(uint32_t*)(_bytes.get()+offset) );
	offset += SEQUENCE_NUMBER_SIZE;
			
	//read the size of the committed checkpoint
	uint32_t temp_bytes_size = ntohl( *(uint32_t*)(_bytes.get() + offset) );
	offset += CHECKPOINTSTATE_SIZE_SIZE;
	
	boost::shared_ptr<char> temp_bytes ( (char*)malloc(temp_bytes_size), free );
	
//	fprintf(stderr, "ViewChange::ViewChange says: syze of the committed CP: %d\n",temp_bytes_size);
	
	//read the bytes of the committed checkpoint
	memcpy( (void*)( temp_bytes.get() ), (const void*)( _bytes.get() + offset ) , temp_bytes_size );
	offset += temp_bytes_size;
	
	committedCP.reset( new BFT::order::statemanagement::CheckPointState(temp_bytes, temp_bytes_size) );
	
	//read the size of the stable checkpoint
	temp_bytes_size = ntohl( *(uint32_t*)(_bytes.get() + offset) );
	offset += CHECKPOINTSTATE_SIZE_SIZE;
	
	temp_bytes.reset( (char*)malloc(temp_bytes_size), free );
	
//	fprintf(stderr, "ViewChange::ViewChange says: syze of the stable CP: %d\n",temp_bytes_size);
	
	//read the bytes of the stable checkpoint
	memcpy( (void*)( temp_bytes.get() ), (const void*)( _bytes.get() + offset ) , temp_bytes_size );
	offset += temp_bytes_size;
	
	stableCP.reset( new BFT::order::statemanagement::CheckPointState(temp_bytes, temp_bytes_size) );
	
	//read the number of entries in the batch and in the histories vectors
	int entry_count = ntohs( *(uint16_t*)(_bytes.get()+offset) );
	offset += ENTRY_COUNT_SIZE;
	
	batch.resize(entry_count);
	histories.resize(entry_count);
	
	//loop over the entries
	for(int i = 0; i < entry_count ; i++)
	{
		//read the size of the certificate entry
		temp_bytes_size = ntohl( *(uint32_t*)(_bytes.get()+offset) );
		offset += CERTIFICATE_ENTRY_SIZE_SIZE;
//		fprintf(stderr, " --- reading entry %d of %d - size %d ---\n",i+1,entry_count,temp_bytes_size);
		
		temp_bytes.reset( (char*)malloc(temp_bytes_size) );
		//read the bytes of the certificate entry
		memcpy( (void*)(temp_bytes.get()) , (const void*)(_bytes.get() + offset) , temp_bytes_size );
		offset += temp_bytes_size;
		
		//create a certificate entry object and store it in batch
		batch.at(i).reset(new BFT::messages::CertificateEntry(temp_bytes, temp_bytes_size, 0));
		
		temp_bytes.reset( (char*)malloc(DIGEST_SIZE));
		//read the bytes of the history digest in this certificate entry
		memcpy( (void*)(temp_bytes.get()) , (const void*)(_bytes.get() + offset) , DIGEST_SIZE);
		offset += DIGEST_SIZE;
		
		//create the history digest object and store it in histories
		histories.at(i).reset(new BFT::messages::HistoryDigest( temp_bytes, DIGEST_SIZE ));
	}
	
	if ((unsigned int) offset != _bytes_size - getAuthenticationSize() )
	{
		fprintf(stderr,"trying to build a ViewChange from invalid bytes\n");
		exit(154);
	}
}

uint32_t ViewChange::computeSize(boost::shared_ptr<BFT::order::statemanagement::CheckPointState> _cp1,
				 boost::shared_ptr<BFT::order::statemanagement::CheckPointState> _cp2,
				 std::vector<boost::shared_ptr<BFT::messages::CertificateEntry> > _entries)
{
	uint32_t size = VIEW_NUMBER_SIZE + //view no
			SEQUENCE_NUMBER_SIZE + // committed cp seqno 
			SEQUENCE_NUMBER_SIZE + // stable cp seqno
			SEQUENCE_NUMBER_SIZE + // last prepared seqno
			SEQUENCE_NUMBER_SIZE + // last preprepared seqno
			CHECKPOINTSTATE_SIZE_SIZE + //committed checkpoint size
			_cp1.get()->computeSize() + //committed chekcpoint
			CHECKPOINTSTATE_SIZE_SIZE + //stable checkpoint size
			_cp2.get()->computeSize() + //stable checkpoint
			ENTRY_COUNT_SIZE; //number of entries in the batch
	int temp_size;
	for (unsigned int i = 0; i < _entries.size() ; i++) //for each entry add
	{	
		_entries.at(i).get()->getBytes(temp_size);
		size += CERTIFICATE_ENTRY_SIZE_SIZE + // the size of the field containing the size of the cert entry
			temp_size + //the size of the certificate entry
			DIGEST_SIZE; //size of the history digest
	}
	
	return size;			
}

bool ViewChange::equals(boost::shared_ptr<ViewChange> _other_vc)
{
	return BFT::messages::MacArrayMessage::equals(_other_vc) 
		&& viewNo == _other_vc.get()->getView()
		&& getCommittedCP().get()->getCurrentSequenceNumber() == 
			_other_vc.get()->getCommittedCP().get()->getCurrentSequenceNumber();
	//TODO implement a REAL equals function. no point in writing it right now...
}

uint32_t ViewChange::maxCompatibleSequenceNumber(boost::shared_ptr<ViewChange> _other_vc)
{
	//TODO thid metod is completely broken! I consider the highest currentseqno-1 as the compatible seqno
	uint32_t other_seqno = _other_vc.get()->getCommittedCP().get()->getCurrentSequenceNumber() -1;
	uint32_t my_seqno = getCommittedCP().get()->getCurrentSequenceNumber() -1;
	return other_seqno>my_seqno?other_seqno:my_seqno;
}

void ViewChange::printInfo()
{
	fprintf(stderr,"ViewChange information...");
	fprintf(stderr,"View number : %d\n", viewNo);
	fprintf(stderr,"Committed CP seqno : %d\n", cpSeqNo);
	fprintf(stderr,"Stable CP seqno : %d\n", stableCPSeqNo);
	fprintf(stderr,"Prepare seqno : %d\n", pSeqNo);
	fprintf(stderr,"Preprepare seqno : %d\n", ppSeqNo);
	committedCP.get()->printInfo();
	stableCP.get()->printInfo();
}


